﻿using RockBus.PerformanceCounters.Interfaces;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace RockBus.PerformanceCounters
{
    public class OperationCounterInternal
    {
        public OperationCounterInternal(string categoryName, string operationName, string instanceName, bool readOnly)
        {
            var counterName = RockBusPerformanceCounterMetadata.GetExecutingCounterName(operationName);
            this._executingCounter = new PerformanceCounter(categoryName, counterName, instanceName, readOnly);
            if (!readOnly)
            {
                this._executingCounter.RawValue = 0;
            }

            counterName = RockBusPerformanceCounterMetadata.GetCompletedCounterName(operationName);
            this._completedCounter = new PerformanceCounter(categoryName, counterName, instanceName, readOnly);
            if (!readOnly)
            {
                this._completedCounter.RawValue = 0;
            }

            counterName = RockBusPerformanceCounterMetadata.GetFailedCounterName(operationName);
            this._failedCounter = new PerformanceCounter(categoryName, counterName, instanceName, readOnly);
            if (!readOnly)
            {
                this._failedCounter.RawValue = 0;
            }

            counterName = RockBusPerformanceCounterMetadata.GetCompletionRateCounterName(operationName);
            this._completionRateCounter = new PerformanceCounter(categoryName, counterName, instanceName, readOnly);
            if (!readOnly)
            {
                this._completionRateCounter.RawValue = 0;
            }

            counterName = RockBusPerformanceCounterMetadata.GetFailureRateCounterName(operationName);
            this._failureRateCounter = new PerformanceCounter(categoryName, counterName, instanceName, readOnly);
            if (!readOnly)
            {
                this._failureRateCounter.RawValue = 0;
            }
        }

        internal readonly PerformanceCounter _executingCounter;
        internal readonly PerformanceCounter _completedCounter;
        internal readonly PerformanceCounter _failedCounter;
        internal readonly PerformanceCounter _completionRateCounter;
        internal readonly PerformanceCounter _failureRateCounter;
    }

    public class OperationCounter : IOperationCounter
    {
        internal OperationCounter(OperationCounterInternal oci)
        {
            this._operationCounterInternal = oci;
        }

        private readonly OperationCounterInternal _operationCounterInternal;

        private int _startGate = 0;
        private int _endGate = 0;

        public void Start()
        {
            if (Interlocked.CompareExchange(ref _startGate, 1, 0) == 1)
            {
                return;
            }

            this._operationCounterInternal._executingCounter.Increment();
        }

        public void Abort()
        {
            if (Interlocked.CompareExchange(ref _endGate, 1, 0) == 1)
            {
                return;
            }

            this._operationCounterInternal._executingCounter.Decrement();
            this._operationCounterInternal._failedCounter.Increment();
            this._operationCounterInternal._failureRateCounter.Increment();
        }

        public void Complete()
        {
            if (Interlocked.CompareExchange(ref _endGate, 1, 0) == 1)
            {
                return;
            }

            this._operationCounterInternal._executingCounter.Decrement();
            this._operationCounterInternal._completedCounter.Increment();
            this._operationCounterInternal._completionRateCounter.Increment();
        }
    }

    public class ReadOnlyOperationCounter : IReadOnlyOperationCounter
    {
        internal ReadOnlyOperationCounter(OperationCounterInternal oci)
        {
            this._operationCounterInternal = oci;
        }

        private readonly OperationCounterInternal _operationCounterInternal;

        public IEnumerable<PerformanceCounter> GetValues()
        {
            yield return this._operationCounterInternal._executingCounter;
            yield return this._operationCounterInternal._completedCounter;
            yield return this._operationCounterInternal._failedCounter;
            yield return this._operationCounterInternal._completionRateCounter;
            yield return this._operationCounterInternal._failureRateCounter;
        }
    }
}